#include "Components/ComponentFlu.h"
#include "Components/ComponentPlayer.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentVitalStats.h"
#include "Components/ComponentSleep.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentCreatureModel.h"

#include "Engine/MathUtils.hpp"

#include "Network/PackageManager.h"
#include "Network/Packages/ComponentFluPackage.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Player/PlayerData.h"

#include "GameRandom.hpp"
#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"


using namespace PocketSurvival::Engine;

namespace PocketSurvival
{

    void ComponentFlu::cough()
    {
        lastCoughTime = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>()->getGameTime();
        coughDuration = 4.0f;
    }

    void ComponentFlu::fluEffect()
    {
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();

        lastEffectTime = timeOfDay->getGameTime();
        float injure = MathUtils::Min(0.1f, componentPlayer->componentHealth->health - 0.175f);
        if (injure > 0.0f)
        {
            componentPlayer->componentHealth->injure(injure, entt::null, false, "着凉打喷嚏", InjureType::Coldness);
        }
        if (coughDuration == 0.0f && (timeOfDay->getGameTime() - lastCoughTime > 40.0 || GameSingleton::gameRandom->getBool(0.5f)))
        {
            cough();
        }
        else if (sneezeDuration == 0.0f)
        {
            sneeze();
        }
    }

    void ComponentFlu::update(float dt)
    {
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        SubsystemGameInfo *gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();

        if (timeOfDay->periodicGameTimeEvent(0.1f, 0.0f))
        {
            std::shared_ptr<IPackage> package = std::make_shared<ComponentFluPackage>(this, FluPackageType::SyncStat);
            package->sendMethod = DeliveryMethod::Unreliable;
            GameSingleton::packageManager->addSendPackageToQueue(package);
        }
        if (componentPlayer->playerData->gameMode == GameMode::Creative || gameInfo->areAdventureSurvivalMechanicsEnabled == false)
        {
            fluDuration = 0.0f;
            fluOnset = 0.0f;
            return;
        }
        if (fluDuration > 0.0f)
        {
            fluOnset = 0.0f;
            float num = 1.0f;
            float temperature = componentPlayer->componentVitalStats->temperature;
            if (temperature > 16.0f)
            {
                num = 2.0f;
            }
            else if (temperature > 12.0f)
            {
                num = 1.5f;
            }
            else if (temperature < 8.0f)
            {
                num = 0.5f;
            }
            fluDuration = MathUtils::Max<float>(fluDuration - num * dt, 0.0f);
            if (componentPlayer->componentHealth->health > 0.0f &&
                componentPlayer->componentSleep->isSleeping() == false &&
                timeOfDay->periodicGameTimeEvent(5.0, 0.0) &&
                timeOfDay->getGameTime() - lastEffectTime > 13.0)
            {
                fluEffect();
                GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentFluPackage>(this, FluPackageType::FluEffect));
            }
        }
        else if (componentPlayer->componentVitalStats->temperature < 6.0f)
        {
            float num2 = 13.0f;
            fluOnset += dt;
            if (fluOnset > 120.0f)
            {
                num2 = 9.0f;
                if (timeOfDay->periodicGameTimeEvent(1.0, 0.0) && GameSingleton::gameRandom->getBool(0.025f))
                {
                    startFlu();
                    GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentFluPackage>(this, FluPackageType::StartFlu));
                }
            }
            if (fluOnset > 60.0f && timeOfDay->periodicGameTimeEvent(num2, -0.01) && GameSingleton::gameRandom->getBool(0.75f))
            {
                sneeze();
                GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentFluPackage>(this, FluPackageType::Sneeze));
            }
        }
        else
        {
            fluOnset = MathUtils::Max<float>(0.0f, fluOnset - dt);
        }
        if ((coughDuration > 0.0f || sneezeDuration > 0.0f) && componentPlayer->componentHealth->health > 0.0f && componentPlayer->componentSleep->isSleeping() == false)
        {
            coughDuration = MathUtils::Max<float>(coughDuration - dt, 0.0f);
            sneezeDuration = MathUtils::Max<float>(sneezeDuration - dt, 0.0f);
            if (GameSingleton::gameRandom->getBool(2.0f * dt))
            {
                Vector3 vector = -1.2f * componentPlayer->componentBody->componentCreature->componentCreatureModel->getEyeRotation().GetForwardVector();
                componentPlayer->componentBody->applyImpulse(vector);
            }
        }
    }

    static const std::string componentName = "Flu";
    const std::string &ComponentFlu::getName() const
    {
        return componentName;
    }
    const std::string &ComponentFlu::GetName()
    {
        return componentName;
    }
    void ComponentFlu::load(const nlohmann::json &jsonData)
    {
        componentPlayer = GameSingleton::gameRegistry->get<ComponentPlayer *>(entity);

        fluDuration = jsonData["FluDuration"].get<float>();
        fluOnset = jsonData["FluOnset"].get<float>();
    }
    void ComponentFlu::save(nlohmann::json &jsonData)
    {
        jsonData["FluDuration"] = fluDuration;
        jsonData["FluOnset"] = fluOnset;
    }
    void ComponentFlu::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "FluDuration", "float", fluDuration);
        MSG_VALUE_SET(jsonData, "FluOnset", "float", fluOnset);
    }

} // namespace PocketSurvival
